﻿using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using Microsoft.IdentityModel.Tokens;

namespace ApiSecurity.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class AuthenticationController : ControllerBase
    {
        public record AuthenticationData(string? Username, string? Password);

        public record UserData(int UserId, string UserName);

        private readonly IConfiguration _config;

        public AuthenticationController(IConfiguration config)
        {
            _config = config;
        }

        [HttpPost("token")]
        public ActionResult<string> Authenticate([FromBody] AuthenticationData data)
        {
            var user = ValidateCredentials(data);
            if (user is null)
            {
                return Unauthorized();
            }

            var token = GenerateToken(user);

            return Ok(token);
        }

        private string GenerateToken(UserData user)
        {
            var secretKey = new SymmetricSecurityKey(
                Encoding.ASCII.GetBytes(_config.GetValue<string>("Authentication:SecretKey")!
                ));
            var signingCredentials = new SigningCredentials(secretKey,
                SecurityAlgorithms.HmacSha256);

            List<Claim> claims = new();
            claims.Add(new Claim(JwtRegisteredClaimNames.Sub, user.UserId.ToString()));
            claims.Add(new Claim(JwtRegisteredClaimNames.UniqueName, user.UserName));

            var token = new JwtSecurityToken(
                _config.GetValue<string>("Authentication:Issuer"),
                _config.GetValue<string>("Authentication:Audience"),
                claims,
                DateTime.UtcNow,
                DateTime.UtcNow.AddMinutes(1),
                signingCredentials
            );
            return new JwtSecurityTokenHandler().WriteToken(token);
        }

        private UserData? ValidateCredentials(AuthenticationData data)
        {
            if (CompareValues(data.Username, "fengliulin") &&
                CompareValues(data.Password, "123"))
            {
                return new UserData(1, data.Username!);
            }

            if (CompareValues(data.Username, "fengliulin1") &&
                CompareValues(data.Password, "123"))
            {
                return new UserData(1, data.Username!);
            }

            return null;
        }

        private bool CompareValues(string? actual, string expected)
        {
            if (actual is not null)
            {
                if (actual.Equals(expected, StringComparison.InvariantCultureIgnoreCase))
                {
                    return true;
                }
            }

            return false;
        }
    }
}